# -*- coding:utf-8 -*-

"""
# 计数器
# 自定义sort函数
    # 1. sortone函数实现,
    # 2. sorttwo函数实现,用一个参数控制顺序
    # 3. sortthree函数实现
    # 4. sortfour函数实现
# 柯里化
"""

# 计数器
'''
def counter(base):
    def inc(step=1):
        base += step
        return base 
    return inc 

f1=counter(5)
f2=counter(5)
print(f1)
print(f2)

'''

# 自定义sort函数
"""
'''
1. 排序问题
    仿照内建函数sorted,实现一个sort函数,能够为列表元素排序.
2. 思路
    内建函数sorted函数是返回一个新的列表,可以设置升序或降序,可以设置一个排序的函数.
    新建一个列表,遍历原列表,和新列表的值依次比较决定如何插入到新列表中.
'''
# 1. sortone函数实现,
def sortone(itera):
    ret = []
    for x in itera:
        for i,y in enumerate(ret):
            if x > y: #找到大的就地插入.
                ret.insert(i,x) #降序
                break
        else: # 不大于,说明最小,尾部追加.
            ret.append(x)
    return ret

print(sortone([1,2,5,4,2,3,5,6]))


# 2. sorttwo函数实现,用一个参数控制顺序
def sorttwo(iterab,reverse=False):
    ret = []
    for x in iterab:
        for i,y in enumerate(ret):
            flag = x > y if reverse else x < y
            if flag: 
                ret.insert(i,x)
                break
        else:
            ret.append(x)
    return ret

print(sorttwo([1,2,5,6,2,3,8,9]))


# 3. sortthree函数实现
def sortthree(iterabl,key = lambda a,b:a > b):
    ret = []
    for x in iterabl:
        for i,y in enumerate(ret):
            if key(x,y): #函数的返回值是bool
                ret.insert(i,x)
                break
        else:
            ret.append(x)
    return ret

print(sortthree([1,2,5,4,8,9]))

# 4. sortfour函数实现
def sortfour(iterables,reverse=False,key=lambda x,y:x<y):
    ret = []
    for x in iterables:
        for i,y in enumerate(ret):
            flag = key(x,y) if not reverse else not key(x,y)
            if flag:
                ret.insert(i,x)
                break
        else:
            ret.append(x)
    return ret

print(sortfour([1,2,7,8,9,10]))


# 柯里化
# 将加法函数柯里化
def add(x,y):
    return x + y 

# 转换如下
def add(x):
    def _add(y):
        return x + y
    return _add

print(add(5)(6))
# 通过嵌套函数可以把函数转换成柯里化函数.

"""

# 柯里化
"""
def add(x,y):
    return x + y 
def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

print(logger(add)(5,y=50))
# 或者换一种写法
add = logger(add)
print(add(x=5,y=10))
"""

# 装饰器语法糖
"""
def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

@logger  # 等价于add = logger(add)
def add(x,y):
    return x + y

print(add(50,55))

# @logger就是装饰器语法
"""

# 装饰器
"""
import datetime,time

def logger(fn):
    def wrap(*args,**kwargs):
        # before功能增强
        print("args={},kwargs={}".format(args,kwargs))
        start = datetime.datetime.now()
        ret = fn(*args,**kwargs)
        # after功能增强
        duration = datetime.datetime.now() - start
        print("function {} took {}s.".format(fn.__name__,duration.total_seconds()))
        return ret
    return wrap

@logger # 相当于add = logger(add)
def add(x,y):
    print("===call add=============")
    time.sleep(2)
    return x + y

print(add(4,y=7))
"""

# 装饰器副作用
"""
def logger(fn):
    def wrapper(*args,**kwargs):
        'I am wrapper'
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x 
    return wrapper

@logger  # add = logger(add)
def add(x,y):
    "this is a function for add"
    return x + y

print("name={},doc={}".format(add.__name__,add.__doc__))
# 原函数对象的属性都被替换了,而使用装饰器,需求是查看被封装函数的属性.
"""

# 提供一个函数,被封装函数属性==copy==> 包装函数属性.
"""
def copy_properties(src,dst): #可以改造成装饰器
    dst.__name__ = src.__name__
    dst.__doc__=src.__doc__

def logger(fn):
    def wrapper(*args,**kwargs):
        'I am wrapper'
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x 
    copy_properties(fn,wrapper)
    return wrapper

@logger # add = logger(add)
def add(x,y):
    '''this is a function for ad '''
    return x + y

print("name={},doc={}".format(add.__name__,add.__doc__))
"""


# 提供一个函数,被封装函数属性==copy==>包装函数属性,改造成带参装饰器
"""
def copy_properties(src):  # 柯里化
    def _copy(dst):
        dst.__name__=src.__name__
        dst.__doc__=src.__doc__
        return dst
    return _copy

def logger(fn):
    @copy_properties(fn)  # wrapper = copy_properties(fn)(wrapper)
    def wrapper(*args,**kwargs):
        'I am wrapper'
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

@logger  # add = logger(add)
def add(x,y):
    '''this is a function for add '''
    return x + y

print("name={},doc={}".format(add.__name__,add.__doc__))


# 带参装饰器
# 获取函数的执行时长,对时长超过阈值的函数记录一下
import datetime,time

def logger(duration):
    def _logger(fn):
        @copy_properties(fn)  # wrapper = wrapper(fn)(wrapper)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            print('so slow') if delta > duration else print('so fast')
            return ret
        return wrapper
    return _logger

@logger(5) # add = logger(5)(add)
def add(x,y):
    time.sleep(3)
    return x + y

print(add(5,6))


# 将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        @copy_properties(fn) #wrapper = wrapper(fn)(wrapper)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return wrapper
    return _logger
"""



# functools模块
"""
import datetime,time,functools

def logger(duration,func=lambda name,duration:print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now()-start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return functools.update_wrapper(wrapper,fn)

    return _logger 

@logger(5)  # add = logger(5)(add)
def add(x,y):
    time.sleep(1)
    return x + y

print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
"""


"""
import datetime,time,functools

def logger(duration,func=lambda name,duration:print('{} took {}s'.format(name,duration))):
    def _logger(fn):
        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now()-start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            return ret
        return wrapper
    return _logger

@logger(5)  # add = logger(5)(add)
def add(x,y):
    time.sleep(1)
    return x + y

print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
"""


# inspect模块
import inspect
"""
'''
signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)
'''
import inspect

def add(x:int,y:int,*args,**kwargs) -> int:
    return x + y

sig = inspect.signature(add)
print(sig,type(sig))  # 函数签名
print('params:',sig.parameters)  # OrderedDict
print('return:',sig.return_annotation)
print(sig.parameters['y'],type(sig.parameters['y']))
print(sig.parameters['x'].annotation)
print(sig.parameters['args'])
print(sig.parameters['args'].annotation)
print(sig.parameters['kwargs'])
print(sig.parameters['kwargs'].annotation)
"""

"""
def add(x,y:int=7,*args,z,t=10,**kwargs) -> int:
    return x + y

sig = inspect.signature(add)
print(sig)
print('params:',sig.parameters) #有序字典
print('return:',sig.return_annotation)
print('~~~~~~~')
for i,item in enumerate(sig.parameters.items()):
    name,param = item
    print(i+1,name,param.annotation,param.kind,param.default)
    print(param.default is param.empty,end='\n\n')
"""

# 业务应用
"""
def add(x,y:int=7) -> int:
    return x + y

def check(fn):
    def wrapper(*args,**kwargs):
        sig = inspect.signature(fn)
        params = sig.parameters
        values = list(params.values())
        for i,p in enumerate(args):
            if isinstance(p,values[i].annotation): #实参和形参声明一致.
                print('==')
        for k,v in kwargs.items():
            if isinstance(v,params[k].annotation): #实参和形参声明一致.
                print('===')
        return fn(*args,**kwargs)
    return wrapper

# 调用测试
check(add)(20,10)
check(add)(20,y=10)
check(add)(y=10,x=20)
"""

"""
def check(fn):
    def wrapper(*args,**kwargs):
        sig = inspect.signature(fn)
        params = sig.parameters
        values = list(params.values())
        for i,p in enumerate(args):
            param = values[i]
            if param.annotation is not param.empty and not isinstance(p,param.annotation):
                print(p,'!==',values[i].annotation)
        for k,v in kwargs.items():
            if params[k].annotation is not inspect._empty and not isinstance(v,params[k].annotation):
                print(k,v,'!===',params[k].annotation)
        return fn(*args,**kwargs)
    return wrapper

@check
def add(x,y:int=7) -> int:
    return x + y


# 调用测试
print(add(20,10))
print(add(20,y=10))
print(add(y=10,x=20))
"""


# partial方法
"""
import functools

def add(x,y) -> int:
    return x + y

newadd = functools.partial(add,y=5)
print(newadd(7))
print(newadd(7,y=6))
print(newadd(y=10,x=6))

print(inspect.signature(newadd))

def add(x,y,*args) -> int:
    print(args)
    return x + y

newadd = functools.partial(add,1,3,6,5)

print(newadd(7))
print(newadd(7,10))
print(newadd(9,10))
print(newadd())
print(inspect.signature(newadd))
"""


# partial函数本质
"""
def partial(func,*args,**keywords):
    def newfunc(*fargs,**fkeywords): #包装函数
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs),**newkeywords)
    newfunc.func = func # 保留原函数
    newfunc.args = args #保留原函数的位置参数.
    newfunc.keywords = keywords  # 保留原函数的关键字参数参数.
    return newfunc

def add(x,y):
    return x + y

foo = partial(add,4)
print(foo(5))
"""


"""
import functools,time
@functools.lru_cache()
def add(x,y,z=3):
    time.sleep(z)
    return x + y

print(add(4,5))
print(add(4,6,3))
print(add(4,y=6))
print(add(x=4,y=6))

"""

# lru_cache装饰器
# 斐波那契数列递归方法的改造
import functools

@functools.lru_cache()  # maxsize = None
def fib(n):
    if n < 3:
        return n
    return fib(n-1) + fib(n-2)

print([fib(x) for x in range(35)])
